﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LEVUtilityLibrary
{
    public class BatteryPack_OneWirePacketFormat
    {
        //========One Wire Data Structure=======================================================
        //========One Wire Transmiting Data Structure=======================================================
        //typedef struct{
        //    static uint cStart1 = 0x80f8;	//起始字元 // send High byte first, then send Low byte second.
        //    static uint cStart2 = 0x80A0;	//function字元 // send High byte first, then send Low byte second.
        //                                    // 0x80A0 : data 資料,
        //                                    // 0x80D0 ~ 0x80DF : EEPROM Seg 0 ~ 15 (a segment = 64 bytes),
        //    unsigned int LenExpected;//數據長度 ; send High byte first, then send Low byte second.
        //    unsigned char DataBuf[LenExpected];//數據內容
        //    unsigned char LRCDataHigh;	//CRC or checkSum High byte, calculating only for DataBuf
        //    unsigned char LRCDataLow;	//CRC or checkSum Low byte, calculating only for DataBuf
        //    static uint cEND1= 0x70f7;	//結束字組 1 // send High byte first, then send Low byte second. 
        //    static uint cEND1= 0x70f7;	//結束字組 2 // send High byte first, then send Low byte second.
        //} ModbusProtocolPacket;//RTU mode
        //========One Wire Transmiting EEPROM Data Structure=======================================================
        //typedef struct{
        //    static uint cStart1 = 0x80f8;	//起始字元 // send High byte first, then send Low byte second.
        //    static uint cStart2 = 0x80D0;	//function字元 // send High byte first, then send Low byte second.
        //    unsigned char DataBuf[];//數據內容
        //    unsigned char LRCDataHigh;	//CRC or checkSum High byte, calculating only for DataBuf
        //    unsigned char LRCDataLow;	//CRC or checkSum Low byte, calculating only for DataBuf
        //    static uint cEND1= 0x70f7;	//結束字組 1 // send High byte first, then send Low byte second. 
        //    static uint cEND1= 0x70f7;	//結束字組 2 // send High byte first, then send Low byte second.
        //} ModbusProtocolPacket;//RTU mode
        //
        /*
		G_Communication_Array[0] = ONE_WIRE_PrecedingCheckCode >> 8;        //high-bytes first
		G_Communication_Array[1] = ONE_WIRE_PrecedingCheckCode & 0x00ff;	//low-bytes second
		G_Communication_Array[2] = ONE_WIRE_Data_PrecedingCode >> 8;        //high-bytes first
		G_Communication_Array[3] = ONE_WIRE_Data_PrecedingCode & 0x00ff;	//low-bytes second
        G_Communication_Array[4] = Data Length      ////high-bytes first
        G_Communication_Array[5] = Data Length      ////low-bytes second
		G_Communication_Array[4 ~ 4 + data_len];
		G_Communication_Array[data_len + 1] = usCRC16 >> 8;			    //high-bytes first
		G_Communication_Array[data_len + 2] = usCRC16;					//low-bytes second
		G_Communication_Array[data_len + 3] = ONE_WIRE_EndCheckCode >> 8;       //high-bytes first
		G_Communication_Array[data_len + 4] = ONE_WIRE_EndCheckCode & 0x00ff;	//low-bytes second
		G_Communication_Array[data_len + 5] = ONE_WIRE_EndCheckCode >> 8;       //high-bytes first
		G_Communication_Array[data_len + 6] = ONE_WIRE_EndCheckCode & 0x00ff;	//low-bytes second
        */
        private const ushort ONE_WIRE_PrecedingCheckCode = (0x80f8);
        private const ushort ONE_WIRE_Data_PrecedingCode = (0x80A0);
        private const ushort ONE_WIRE_EEPROM_Seg_PrecedingCode = (0x80D0);
        private const ushort ONE_WIRE_EndCheckCode = (0x70f7);

        public enum OneWireDataGroup
        {
            None,
            LEV_ST_G2_OneWire_SystemData_Group,
            LEV_ST_G2_OneWire_EEPROM_Group
        }
        private static bool data_CRC16_Check(byte[] data_with_crc16, out byte[] data_without_crc)
        {
            byte crc_hi, crc_lo;
            int crc_hi_idx, crc_lo_idx;
            crc_hi_idx = data_with_crc16.Length - 2;
            crc_lo_idx = data_with_crc16.Length - 1;
            crc_hi = data_with_crc16[crc_hi_idx];
            crc_lo = data_with_crc16[crc_lo_idx];
            data_without_crc = new byte[0];
            ushort receiveCRC = (ushort)((crc_hi << 8) + crc_lo);
            byte[] checkData = new byte[data_with_crc16.Length - 2];
            Array.Copy(data_with_crc16, 0, checkData, 0, data_with_crc16.Length - 2);

            ushort crc16 = MassUtilities.ComputeModBusCrc16(checkData);
            if (receiveCRC == crc16)
            {
                data_without_crc = (byte[])checkData.Clone();
                return true;
            }
            return false;
        }


        private static void Packet_Decoding_To_Group_And_Clear_List(ref List<byte> RawData, out List<OneWireDataGroup> decoding_Group, out List<byte[]> decoding_Data_List)
        {
            int segStartIndex = 0;
            int segEndIndex = 0;
            int dataStartIndex = 0;
            int dataEndIndex = 0;
            byte[] decoding_Data = new byte[0];

            decoding_Group = new List<OneWireDataGroup>();
            decoding_Data_List = new List<byte[]>();
            int RemoveEndIndex = 0;


            byte ONE_WIRE_PrecedingCheckCode_HighByte = (byte)(ONE_WIRE_PrecedingCheckCode >> 8);
            byte ONE_WIRE_PrecedingCheckCode_LowByte;
            byte ONE_WIRE_Data_PrecedingCode_HighByte = (byte)(ONE_WIRE_Data_PrecedingCode >> 8);
            byte ONE_WIRE_Data_PrecedingCode_LowByte;
            byte ONE_WIRE_EEPROM_Seg_PrecedingCode_HighByte = (byte)(ONE_WIRE_EEPROM_Seg_PrecedingCode >> 8);
            byte ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte;
            byte ONE_WIRE_EndCheckCode_HighByte = (byte)(ONE_WIRE_EndCheckCode >> 8);
            byte ONE_WIRE_EndCheckCode_LowByte;
            unchecked
            {
                ONE_WIRE_PrecedingCheckCode_LowByte = (byte)ONE_WIRE_PrecedingCheckCode;
                ONE_WIRE_Data_PrecedingCode_LowByte = (byte)ONE_WIRE_Data_PrecedingCode;
                ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte = (byte)ONE_WIRE_EEPROM_Seg_PrecedingCode;
                ONE_WIRE_EndCheckCode_LowByte = (byte)ONE_WIRE_EndCheckCode;
            }
            bool StartSrting = false;
            bool EndSrting = false;
            OneWireDataGroup status = OneWireDataGroup.None;
            byte[] receivingRawData = RawData.ToArray<byte>();
            for (int i = 0; i < receivingRawData.Length - 3; i++)
            {
                if ((StartSrting == false) && (receivingRawData[i] == ONE_WIRE_PrecedingCheckCode_HighByte) && (receivingRawData[i + 1] == ONE_WIRE_PrecedingCheckCode_LowByte))
                {
                    if ((receivingRawData[i + 2] == ONE_WIRE_Data_PrecedingCode_HighByte) &&
                        (receivingRawData[i + 3] == ONE_WIRE_Data_PrecedingCode_LowByte))
                    {
                        status = OneWireDataGroup.LEV_ST_G2_OneWire_SystemData_Group;
                        dataStartIndex = i + 4;
                        segStartIndex = i;
                        StartSrting = true;
                        //break;
                    }
                    else if ((receivingRawData[i + 2] == ONE_WIRE_EEPROM_Seg_PrecedingCode_HighByte) &&
                             (receivingRawData[i + 3] == ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte))
                    {
                        status = OneWireDataGroup.LEV_ST_G2_OneWire_EEPROM_Group;
                        dataStartIndex = i + 4;
                        segStartIndex = i;
                        StartSrting = true;
                        //break;
                    }
                }//if ((receivingRawData[i] == ONE_WIRE_PrecedingCheckCode_HighByte) && (receivingRawData[i + 1] == ONE_WIRE_PrecedingCheckCode_LowByte))
                if ((StartSrting) && (EndSrting == false))
                {
                    if ((receivingRawData[i] == ONE_WIRE_EndCheckCode_HighByte) &&
                        (receivingRawData[i + 1] == ONE_WIRE_EndCheckCode_LowByte) &&
                        (receivingRawData[i + 2] == ONE_WIRE_EndCheckCode_HighByte) &&
                        (receivingRawData[i + 3] == ONE_WIRE_EndCheckCode_LowByte))
                    {
                        dataEndIndex = i - 1;
                        segEndIndex = i + 3;
                        EndSrting = true;
                        //break;
                    }
                }//if ((status == Received_Data_Group.OneWire_SystemData_Group) || (status == Received_Data_Group.OneWire_EEPROM_Group))
                // Packet Form Found
                if (StartSrting && EndSrting)
                {
                    int length = dataEndIndex - dataStartIndex + 1;
                    decoding_Data = new byte[length];
                    Array.Copy(receivingRawData, dataStartIndex, decoding_Data, 0, length);
                    if (data_CRC16_Check(decoding_Data, out decoding_Data))
                    {
                        decoding_Group.Add(status);
                        decoding_Data_List.Add(decoding_Data);
                        RemoveEndIndex = segEndIndex;
                    }
                    status = OneWireDataGroup.None;
                    dataEndIndex = 0;
                    dataStartIndex = 0;
                    StartSrting = false;
                    EndSrting = false;
                    segEndIndex = 0;
                    segStartIndex = 0;
                }
            }//for

            //Remove found Data
            if (decoding_Group.Count >= 1)
            {
                RawData.RemoveRange(0, RemoveEndIndex + 1);
            }
        }//public static void Packet_Decoding_To_Group_And_Clear_List

        public static OneWireDataGroup UnPacking(byte[] ReceivedRawData, out byte[] UnpackingData)
        {
            int segStartIdxOfRawData = 0;
            int segEndIdxOfRawData = 0;
            int dataStartIdxOfRawData = 0;
            int dataEndIdxOfRawData = 0;
            return UnPacking(ReceivedRawData, out UnpackingData, out segStartIdxOfRawData, out segEndIdxOfRawData, out dataStartIdxOfRawData, out dataEndIdxOfRawData);
        }
        public static OneWireDataGroup UnPacking(byte[] ReceivedRawData, out byte[] UnpackingData, out int segStartIdxOfRawData, out int segEndIdxOfRawData, out int dataStartIdxOfRawData, out int dataEndIdxOfRawData)
        {
            segStartIdxOfRawData = 0;
            segEndIdxOfRawData = 0;
            dataStartIdxOfRawData = 0;
            dataEndIdxOfRawData = 0;
            bool formFound = false;
            UnpackingData = new byte[0];

            OneWireDataGroup status = OneWireDataGroup.None;

            byte ONE_WIRE_PrecedingCheckCode_HighByte = (byte)(ONE_WIRE_PrecedingCheckCode >> 8);
            byte ONE_WIRE_PrecedingCheckCode_LowByte;
            byte ONE_WIRE_Data_PrecedingCode_HighByte = (byte)(ONE_WIRE_Data_PrecedingCode >> 8);
            byte ONE_WIRE_Data_PrecedingCode_LowByte;
            byte ONE_WIRE_EEPROM_Seg_PrecedingCode_HighByte = (byte)(ONE_WIRE_EEPROM_Seg_PrecedingCode >> 8);
            byte ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte;
            byte ONE_WIRE_EndCheckCode_HighByte = (byte)(ONE_WIRE_EndCheckCode >> 8);
            byte ONE_WIRE_EndCheckCode_LowByte;
            unchecked
            {
                ONE_WIRE_PrecedingCheckCode_LowByte = (byte)ONE_WIRE_PrecedingCheckCode;
                ONE_WIRE_Data_PrecedingCode_LowByte = (byte)ONE_WIRE_Data_PrecedingCode;
                ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte = (byte)ONE_WIRE_EEPROM_Seg_PrecedingCode;
                ONE_WIRE_EndCheckCode_LowByte = (byte)ONE_WIRE_EndCheckCode;
            }
            for (int i = 0; i < ReceivedRawData.Length - 4; i++)
            {
                if ((ReceivedRawData[i] == ONE_WIRE_PrecedingCheckCode_HighByte) && (ReceivedRawData[i + 1] == ONE_WIRE_PrecedingCheckCode_LowByte))
                {
                    if ((ReceivedRawData[i + 2] == ONE_WIRE_Data_PrecedingCode_HighByte) &&
                        (ReceivedRawData[i + 3] == ONE_WIRE_Data_PrecedingCode_LowByte))
                    {
                        status = OneWireDataGroup.LEV_ST_G2_OneWire_SystemData_Group;
                        dataStartIdxOfRawData = i + 4;
                        segStartIdxOfRawData = i;
                        break;
                    }
                    else if ((ReceivedRawData[i + 2] == ONE_WIRE_EEPROM_Seg_PrecedingCode_HighByte) &&
                             (ReceivedRawData[i + 3] == ONE_WIRE_EEPROM_Seg_PrecedingCode_LowByte))
                    {
                        status = OneWireDataGroup.LEV_ST_G2_OneWire_EEPROM_Group;
                        dataStartIdxOfRawData = i + 4;
                        segStartIdxOfRawData = i;
                        break;
                    }
                    else
                    {
                        return OneWireDataGroup.None;
                    }
                }
            }//for
            if ((status == OneWireDataGroup.LEV_ST_G2_OneWire_SystemData_Group) || (status == OneWireDataGroup.LEV_ST_G2_OneWire_EEPROM_Group))
            {
                for (int j = dataStartIdxOfRawData; j < ReceivedRawData.Length - 3; j++)
                {
                    if(j==70){
                        formFound = false;
                    }
                    if ((ReceivedRawData[j] == ONE_WIRE_EndCheckCode_HighByte) &&
                        (ReceivedRawData[j + 1] == ONE_WIRE_EndCheckCode_LowByte) &&
                        (ReceivedRawData[j + 2] == ONE_WIRE_EndCheckCode_HighByte) &&
                        (ReceivedRawData[j + 3] == ONE_WIRE_EndCheckCode_LowByte))
                    {
                        formFound = true;
                        dataEndIdxOfRawData = j - 1;
                        segEndIdxOfRawData = j + 3;
                        break;
                    }
                }//for
            }//if
            if (formFound)
            {
                int length = dataEndIdxOfRawData - dataStartIdxOfRawData + 1;
                UnpackingData = new byte[length];
                Array.Copy(ReceivedRawData, dataStartIdxOfRawData, UnpackingData, 0, length);
                if (data_CRC16_Check(UnpackingData, out UnpackingData))
                {
                    dataEndIdxOfRawData -= 2;   // exclude CRC 2 bytes
                    return status;
                }
                else
                {
                    UnpackingData = new byte[0];
                    segStartIdxOfRawData = 0;
                    segEndIdxOfRawData = 0;
                    dataStartIdxOfRawData = 0;
                    dataEndIdxOfRawData = 0;
                    return OneWireDataGroup.None;
                }
            }
            segStartIdxOfRawData = 0;
            segEndIdxOfRawData = 0;
            dataStartIdxOfRawData = 0;
            dataEndIdxOfRawData = 0;
            return OneWireDataGroup.None;
        }//public static Received_Data_Group Packet_Decoding(byte[] receivingRawData, out byte[] decoding_Data)
    }//public class BatteryPack_OneWirePacketFormat
}//namespace LEVUtilityLibrary
